using System;
using System.IO;
using System.Text;

namespace CricketDuck_Standard_CLI
{
	class MainClass
	{
		public static string traceback = "";
		public static int Main (string[] args)
		{
			try
			{
				Console.Write("What is the G50 value? (Enter 245 if not sure.) ");
				int G50 = int.Parse(Console.ReadLine());
				Console.Write ("Number of overs in the first innings (before interruptions): ");
				string N = ReadOvers();
				if (!IsValidOverCount (N))
					throw new Exception ("Number of overs is invalid or too high.");
				double R1 = GetResources (N, 0);
				Console.Write("What was the first innings score? ");
				int S = int.Parse(Console.ReadLine());
				Console.Write ("Was the first innings interrupted? (y/N) ");
				if (Console.ReadLine ().ToLower () == "y") {
					Console.Write("When did this interruption occur? ");
					string interruption_time = ReadOvers();
					Console.Write("How many wickets had been lost at the time? ");
					int wickets_lost = int.Parse(Console.ReadLine());
					Console.Write("What was the new over count? ");
					string new_over_count = ReadOvers();
					double resources_lost = GetResources(SubtractOvers(N, interruption_time), wickets_lost) - GetResources(SubtractOvers(new_over_count, interruption_time), wickets_lost);
					R1 -= resources_lost;
				}
				Console.Write("How many overs did the team batting second have (prior to interruptions)? ");
				string N2 = ReadOvers();
				Console.WriteLine("Pick an option:");
				Console.WriteLine("[1] Calculate Par score.");
				Console.WriteLine("[2] Calculate second innings target.");
				switch(Console.ReadLine())
				{
				case "1":
				{
					Console.Write("When was the second innings interrupted? ");
					string second_interruption = ReadOvers();
					Console.Write("How many wickets had the team batting second lost at the time? ");
					int wickets_lost = int.Parse(Console.ReadLine());
					double R2 = GetResources(N2, 0) - GetResources(SubtractOvers(N2, second_interruption), wickets_lost);
					int newTarget = CalculateNewTarget(S, R1, R2, G50);
					Console.WriteLine("Team 2 needed " + newTarget + " to win or " + (newTarget - 1) + " to tie from " + second_interruption + " overs.");
					return 0;
				}
				case "2":
				{
					double R2 = GetResources(N2, 0);
					Console.Write("Was the second innings interrupted? (y/N) ");
					if(Console.ReadLine().ToLower() == "y")
					{
						Console.Write("How many overs had passed at the time of the interruption? ");
						string overs_remaining1 = SubtractOvers(N2, ReadOvers());
						Console.Write("How many wickets had been lost at the time? ");
						int wickets_lost = int.Parse(Console.ReadLine());
						Console.Write("What was the new over count after the interruption? ");
						string overs_remaining2 = ReadOvers();
						if(SubtractOvers(N2, overs_remaining2).Contains("-"))
						{
							throw new Exception("Revised over count cannot be above original over count.");
						}
						overs_remaining2 = SubtractOvers(overs_remaining2, SubtractOvers(N2, overs_remaining1));
						R2 -= GetResources(overs_remaining1, wickets_lost) - GetResources(overs_remaining2, wickets_lost);
					}
					int newTarget = CalculateNewTarget(S, R1, R2, G50);
					Console.WriteLine("Team 2 need " + newTarget + " to win or " + (newTarget - 1) + " to tie.");
					return 0;
				}
				default:
				{
					throw new Exception("Invalid option.");
				}
				}
			}
			catch(Exception e) {
				writeTraceback("At Main(string[]):");
				Console.Error.WriteLine ("Traceback (most recent call last):");
				Console.Error.Write (traceback.ToString());
				Console.Error.WriteLine ("Error: " + e.GetType () + " occurred.");
				Console.Error.WriteLine ("Description: " + e.Message);
				return -1;
			}
		}
		protected static double GetResources(string overs_remaining, int wickets_lost)
		{
			try
			{
				// Gets the resource percentage for a given number of overs remaining and wickets lost.
				using (Stream database = File.OpenRead(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly ().Location) + "/table_of_resources.csv"))
					using (StreamReader databaseReader = new StreamReader(database)) {
					return Parser.parseDouble (Grep (overs_remaining + "," + wickets_lost + ",", databaseReader).Replace (overs_remaining + "," + wickets_lost + ",", ""));
				}
			}
			catch(Exception e) {
				writeTraceback ("At GetResources(string, int):");
				throw e;
			}
		}
		private static string Grep(string s, StreamReader reader)
		{
			try
			{
				// Note: This is not suitable as a C# version of the Unix 'grep'. It is more of an implementation of 'grep -c 1'.
				while (!reader.EndOfStream) {
					string s2 = reader.ReadLine ();
					if (s2.StartsWith(s))
						return s2;
				}
				throw new EndOfStreamException ();
			}
			catch(Exception e) {
				writeTraceback ("At Grep(string, StreamReader):");
				throw e;
			}
		}
		protected static int CalculateNewTarget(int FirstInningsTotal, double R1, double R2, int G50)
		{
			try
			{
				// R1 is the resource percentage available to the team batting first.
				// R2 is the resource percentage available to the team batting second.
				if (R2 < R1) {
					return (int)((FirstInningsTotal * (R2 / R1)) + 1);
				} else if (R2 == R1) {
					return FirstInningsTotal + 1;
				} else if (R2 > R1) {
					return (int)Math.Ceiling(FirstInningsTotal + ((R2 - R1) * (G50/100d)) + 1);
				}
				throw new Exception ("An unknown error occurred while calculating the new target.");
			}
			catch(Exception e) {
				writeTraceback ("At CalculateNewTarget(int, double, double):");
				throw e;
			}
		}
		private static bool IsValidOverCount (string n)
		{
			try
			{
				double oversAsDouble = Parser.parseDouble(n);
				int oversAsInteger = (int)oversAsDouble;
				if(oversAsDouble != Math.Abs(oversAsDouble) || Math.Abs(oversAsDouble - oversAsInteger) > 0.6 || (oversAsDouble.ToString().Split('.').Length > 1 && oversAsDouble.ToString().Split('.')[1].Length > 1) || oversAsDouble > 50)
				{
					return false;
				}
				return true;
			}
			catch {
				return false;
			}
		}
		protected static string SubtractOvers(string minuend, string subtrahend)
		{
			try
			{
				if (!IsValidOverCount (minuend) || !IsValidOverCount (subtrahend))
					throw new FormatException ();
				int m_minuend = (int.Parse (minuend.Split ('.') [0]) * 6) + ((int)minuend.Split ('.') [1] [0] - 48);
				int m_subtrahend = (int.Parse (subtrahend.Split ('.') [0]) * 6) + ((int)subtrahend.Split ('.') [1] [0] - 48);
				return BallsToOvers (m_minuend - m_subtrahend);
			}
			catch(Exception e) {
				writeTraceback ("At SubtractOvers(string, string):");
				throw e;
			}
		}
		private static string BallsToOvers (int i)
		{
			try
			{
				return (int)(i / 6d) + "." + (i % 6);
			}
			catch(Exception e) {
				writeTraceback ("At BallsToOvers(int):");
				throw e;
			}
		}

		static void writeTraceback (string data)
		{
			traceback += "    " + data + "\n";
		}
		public static string ReadOvers()
		{
			string s = Console.ReadLine ();
			return s.Contains (".") ? s : s + ".0";
		}
	}
}